package utils.math_utils;

import java.util.Random;

import Jama.EigenvalueDecomposition;
import Jama.Matrix;

public class _Matrix extends Matrix {

	private static final long serialVersionUID = 7293304642196503956L;
	private int m;
	private int n;
	private double[][] M;
	public Random Randi = new Random();

	public _Matrix(int m, int n) {
		super(m, n);
		this.m = m;
		this.n = n;

	}

	public _Matrix(int m, int n, double z) {
		this(m, n);
		for (int i = 0; i < m; i++) {
			for (int j = 0; j < n; j++) {
				M[i][j] = z;
			}
		}
	}

	public _Matrix(double[][] Ar) {
		super(Ar);
		this.m = Ar.length;
		this.n = Ar[0].length;
	}

	public void Zero() {
		for (int i = 0; i < m; i++) {
			for (int j = 0; j < n; j++) {
				M[i][j] = 0.0;
			}
		}
	}

	public void rand() {
		for (int i = 0; i < m; i++) {
			for (int j = 0; j < n; j++) {
				double R = Math.random();
				M[i][j] = R;
			}
		}
	}

	public void rand(double max, double min) {
		for (int i = 0; i < m; i++) {
			for (int j = 0; j < n; j++) {
				double R = (max - min) * Math.random() + min;
				M[i][j] = R;
			}
		}
	}

	public void Sprand(double sparsity, double max, double min) {
		if (sparsity > 1.0 || sparsity < 0) {
			throw new IllegalArgumentException("Cannot have sparsity greater"
					+ " than 1 or less than 0.");
		} else {
			for (int i = 0; i < m; i++) {
				for (int j = 0; j < n; j++) {
					double rand = Math.random();
					if (rand <= sparsity) {
						double s = (max - min) * Math.random() + min;
						this.set(i, j, s);
					} else
						this.set(i, j, 0.0);
				}
			}
		}
	}

	public void SparseSpecific(int numPerColumn, double min, double max) {
		if (numPerColumn <= 0) {
			throw new IllegalArgumentException("Must have more than zero "
					+ "entries per column");
		} else {
			this.Zero();
			int Ri = Randi.nextInt(m);
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < numPerColumn; j++) {
					do {
						Ri = Randi.nextInt(m);
					} while (get(Ri, i) != 0.0);
					double R = (max - min) * Math.random() + min;
					set(Ri, i, R);
				}
			}
		}
	}

	public void ScaleEig(double Eigen) {
		double MaxEig = 0.0;
		EigenvalueDecomposition Eig = this.eig();
		double[] Ev = Eig.getRealEigenvalues();
		for (int i = 0; i < Ev.length; i++) {
			if (Math.abs(Ev[i]) > MaxEig)
				MaxEig = Math.abs(Ev[i]);
		}
		this.timesEquals(Eigen).timesEquals(1.0 / MaxEig);

	}

	public double SpectralRad() {
		double MaxEig = 0.0;
		EigenvalueDecomposition Eig = this.eig();
		double[] Ev = Eig.getRealEigenvalues();
		for (int i = 0; i < Ev.length; i++) {
			if (Math.abs(Ev[i]) > MaxEig)
				MaxEig = Math.abs(Ev[i]);
		}
		return MaxEig;
	}

	public void setRow(int row, double [] R){
		
		if(R.length != this.getColumnDimension())
			throw new IllegalArgumentException("Dims don't match. \n" + 
					"In: " + R.length + " col: " + this.getColumnDimension());
		
		for(int i = 0; i < this.getColumnDimension(); i++){
			set(row, i, R[i]);
		}
		
	}

	public double get(int m, int n) {
		return M[m][n];
	}
	
	public static int MaxIndex(double [] vec){
		double max = Double.MIN_VALUE;
		int z = 0;
		for(int i = 0; i < vec.length; i++){
			if(vec[i] > max){
				max = vec[i];
				z = i;
			}
		}
		return z;
	}
	
	public static double norm(double [] vec){
		double norm = 0.0;
		for(int i = 0; i < vec.length; i++)
			norm += vec[i]*vec[i];
		
		if(norm <= 0)
			System.out.println("Norm is non-positive.");
		return Math.sqrt(norm);
	}
	
	public static double dotProduct(double [] vec1, double [] vec2){
		double dotProd = 0.0;
		if(vec1.length != vec2.length){
			throw new IllegalArgumentException("Input Mismatch, vector" +
					" dimenesions must agree.");
		}else{
			for(int i = 0; i < vec1.length; i++)
				dotProd += vec1[i]*vec2[i];
		}
		
		if(dotProd == Double.POSITIVE_INFINITY || dotProd == Double.NEGATIVE_INFINITY){
			System.out.println("Dot Product is infinite");
		}
		return dotProd;
	}
	
	public static double[] Concat(double [] Array1, double [] Array2){
		double [] Cat = new double [Array1.length+Array2.length];
		for(int i = 0; i < Cat.length; i++){
			if(i < Array1.length)
				Cat[i] = Array1[i];
			else
				Cat[i] = Array2[i-Array1.length];
		}
		return Cat;
	}
	
	public static double [][] GetSubArray(int Ro, int Co, int Rf, int Cf, 
			double [][] Ar){
		double[][] Sub = new double[Rf-Ro][Cf-Co];
		for(int i = Ro; i < Rf; i++){
			for(int j = Co; j < Cf; j++){
				Sub[i-Ro][j-Co] = Ar[i][j] - 0.5;
			}
		}
		return Sub;
	}
}

